#include "zendjinn.h"

DEFINE_SINGLETON(ZenDjinn);

ZenDjinn::ZenDjinn(const string& name, ClassId* classId):
Instance(name, classId),
mRunning(false),
mFPS(60.0f)
{
}

ZenDjinn::~ZenDjinn()
{
}

bool ZenDjinn::init()
{
    atexit(ZenDjinn::quit);
    try {
        // Initialize SDL
        if (SDL_Init(SDL_INIT_EVERYTHING) == -1)
            throw (SDL_GetError());

        // Initialize Joysticks
        if(!Joystick::detectActive())
            throw ("Could not detect joysticks");

        // Initialize Haptics
        if(!Haptic::detectActive())
            throw ("Could not detect haptic devices");
    }
    catch (const char *error) {
        cerr << "ZenDjinn::init() : " << error << endl;
        return false;
    }
    return true;
}

void ZenDjinn::quit()
{
    Keyboard::setActive(0);
    Mouse::setActive(0);
    //Joystick::clearActive();
    //Haptic::clearActive();

    SDL_Quit();
}

void ZenDjinn::run()
{
    SDL_Event event;
    uint64_t then = SDL_GetPerformanceCounter();

    assert(SDL_WasInit(SDL_INIT_TIMER));

    mRunning = true;
    while(mRunning) {
        uint64_t now = SDL_GetPerformanceCounter();
        float dt = (now - then) / (float)SDL_GetPerformanceFrequency();
        then = now;

        // Limit dt
        if(dt > 0.1f)
            dt = 1.0f / mFPS;

        // Event handling
        while(SDL_PollEvent(&event))
            handleEvent(&event, dt);

        // Update everything
        if(dt > 0.0f)
            update(dt);

        // Signal all the render threads
        if(Window::sIdMapMutex) {
            map<Uint32,Window*>::iterator i;
            for(i=Window::sIdMap.begin(); i!= Window::sIdMap.end(); i++) {
                Window* window = Window::getFromId(i->first);
                if(window)
                    window->nextFrame();
            }
        }
        SDL_Delay(30);
    }
}

void ZenDjinn::update(float dt)
{
    // TODO : Advance animation / simulation in time
}

void ZenDjinn::handleEvent(SDL_Event* event, float dt)
{
    if(!event)
        return;
    switch (event->type) {
        // Quit event
        case SDL_QUIT:
            DEBUG_MARKER;
            if(Window::sIdMap.size())
                mRunning = false;
            break;

        // Window events
        case SDL_WINDOWEVENT:
            Window::handleEvent((SDL_WindowEvent*)event, dt);
            break;

        // Keyboard events
        case SDL_KEYDOWN:
        case SDL_KEYUP:
            Keyboard::handleKeyEvent((SDL_KeyboardEvent*)event, dt);
            break;
        case SDL_TEXTINPUT:
            Keyboard::handleTextInputEvent((SDL_TextInputEvent*)event, dt);
            break;

        // Mouse events
        case SDL_MOUSEBUTTONDOWN:
        case SDL_MOUSEBUTTONUP:
            Mouse::handleButtonEvent((SDL_MouseButtonEvent*)event, dt);
            break;
        case SDL_MOUSEMOTION:
            Mouse::handleMotionEvent((SDL_MouseMotionEvent*)event, dt);
            break;
        case SDL_MOUSEWHEEL:
            Mouse::handleWheelEvent((SDL_MouseWheelEvent*)event, dt);
            break;

        // Joystick events
        case SDL_JOYAXISMOTION:
            Joystick::handleAxisEvent((SDL_JoyAxisEvent*)event, dt);
            break;
        case SDL_JOYBALLMOTION:
            Joystick::handleBallEvent((SDL_JoyBallEvent*)event, dt);
            break;
        case SDL_JOYHATMOTION:
            Joystick::handleHatEvent((SDL_JoyHatEvent*)event, dt);
            break;
        case SDL_JOYBUTTONDOWN:
        case SDL_JOYBUTTONUP:
            Joystick::handleButtonEvent((SDL_JoyButtonEvent*)event, dt);
            break;

        // Touch events
        /*
        case SDL_FINGERDOWN:
        case SDL_FINGERUP:
        case SDL_FINGERMOTION:
            Touch::handleFingerEvent((SDL_TouchFingerEvent*)event, dt);
            break;
        case SDL_TOUCHBUTTONDOWN:
        case SDL_TOUCHBUTTONUP:
            Touch::handleButtonEvent((SDL_TouchButtonEvent*)event, dt);
            break;
        case SDL_DOLLARGESTURE:
            Touch::handleSingleGestureEvent((SDL_DollarGestureEvent*)event, dt);
            break;
        case SDL_MULTIGESTURE:
            Touch::handleMultiGestureEvent((SDL_MultiGestureEvent*)event, dt);
            break;
        */

        // User events
        case SDL_USEREVENT:
            break;
    }
}

bool ZenDjinn::load(DataStream* ds)
{
    INSTANCE_LOAD(ds);
    Instance* instance;

    try {
        if (!(*ds >> mFPS))
            throw ("Could not load FPS");
        if (!(*ds >> instance))
            throw ("Could not load Keyboard");
        Keyboard::setActive((Keyboard*)instance);
        if (!(*ds >> instance))
            throw ("Could not load Mouse");
        Mouse::setActive((Mouse*)instance);
    }
    catch (const char* error) {
        cerr << "ZenDjinn::load(ds) : " << error << endl;
        return false;
    }

    return true;
}

bool ZenDjinn::save(DataStream* ds)
{
    INSTANCE_SAVE(ds);
    Instance* instance;

    try {
        if (!(*ds << mFPS))
            throw ("Could not save FPS");
        instance = (Instance*) Keyboard::getActive();
        if(!(*ds << instance))
            throw ("Could not save Keyboard");
        instance = (Instance*) Mouse::getActive();
        if(!(*ds << instance))
            throw ("Could not save Mouse");
    }
    catch (const char* error) {
        cerr << "ZenDjinn::save(ds) : " << error << endl;
        return false;
    }

    return true;
}
